package com.djq.hhc.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.djq.hhc.feign.CreditInfoFeignClient;
import com.djq.hhc.holder.UserHolder;
import com.djq.hhc.mapper.ShopGoodsMapper;
import com.djq.hhc.model.shop.ShopGoods;
import com.djq.hhc.model.shop.ShopOrder;
import com.djq.hhc.redis.CacheClient;
import com.djq.hhc.redis.RedisIdWorker;
import com.djq.hhc.redis.SimpleRedisLock;
import com.djq.hhc.result.Result;
import com.djq.hhc.service.ShopGoodsService;
import com.djq.hhc.service.ShopOrderService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.LinkedHashMap;
import java.util.concurrent.TimeUnit;

import static com.djq.hhc.redis.RedisConstants.*;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: dadd7
 * @Date: 2023/03/05/下午 4:56
 * @Description:
 */

@Service
public class ShopGoodsServiceImpl extends ServiceImpl<ShopGoodsMapper, ShopGoods> implements ShopGoodsService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisIdWorker redisIdWorker;

    @Autowired
    private ShopOrderService shopOrderService;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private CreditInfoFeignClient creditInfoFeignClient;

    @Autowired
    private CacheClient cacheClient;

    @Override
    @Transactional
    public Result updateGoodsById(ShopGoods shopGoods) {
        Long id = shopGoods.getId();
        if (id == null) {
            return Result.fail().message("商品id不能为空");
        }
        String goodsName = shopGoods.getGoodsName();
        ShopGoods one = query().eq("goods_name", goodsName).ne("id", id).one();
        if (one == null) {
            updateById(shopGoods);
            stringRedisTemplate.delete(SHOP_GOODS_KEY + id);
            return Result.ok().message("商品更新成功");
        } else {
            return Result.fail().message(goodsName + "已存在");
        }
    }

    @Override
    public Result saveGoods(ShopGoods shopGoods) {
        String goodsName = shopGoods.getGoodsName();
        ShopGoods one = query().eq("goods_name", goodsName).one();
        if (one == null) {
            save(shopGoods);
            return Result.ok().message("商品添加成功");
        } else {
            return Result.fail().message(goodsName + "已存在");
        }
    }

    @Override //解决缓存穿透
    public Result getGoodsById(Long id) {
        //从redis中获取
        String goodsJson = stringRedisTemplate.opsForValue().get(SHOP_GOODS_KEY + id);
        //判断是否有值
        if (StrUtil.isNotBlank(goodsJson)) {
            ShopGoods goods = JSONUtil.toBean(goodsJson, ShopGoods.class);
            return Result.ok(goods);
        }
        //判断是否是空值，到这里goodsJson只剩两种情况，一种是“”,另一种是null
        if (goodsJson != null) {
            return Result.fail("查询商品错误，没有这个商品");
        }
        //不存在，查数据库
        ShopGoods shopGoods = getById(id);
        //数据库中也不存在，将redis放“”
        if (shopGoods == null) {
            stringRedisTemplate.opsForValue().set(SHOP_GOODS_KEY + id, "", CACHE_NULL_TTL, TimeUnit.MINUTES);
            return Result.fail("查询商品错误，没有这个商品");
        }
        //数据库有值，存入redis,返回
        stringRedisTemplate.opsForValue().set(SHOP_GOODS_KEY + id, JSONUtil.toJsonStr(shopGoods), SHOP_GOODS_TTL, TimeUnit.HOURS);
        return Result.ok(shopGoods);
    }

    //这个，商品，一般是不能删除的，因为删除了，用户的账单那里就会出错
    //只能售罄，库存为0就不能买了
    @Override
    @Transactional
    public Result removeGoodsById(Integer id) {
        if (id == null) {
            return Result.fail().message("商品id不能为空");
        }
        removeById(id);
        stringRedisTemplate.delete(SHOP_GOODS_KEY + id);
        return Result.ok().message("商品删除成功");
    }

    @Override
    public Result buyGoods(String token, Long id) {
        Result goodsById = this.getGoodsById(id);
        if (goodsById.getData() == null) {
            return Result.fail().message("商品不存在");
        }
        // 这里不是LinkedHashMap<String, Object>
        // LinkedHashMap<String, Object> data = (LinkedHashMap<String, Object>) goodsById.getData();
        ShopGoods goods = (ShopGoods) goodsById.getData();
        //库存是否充足
        Integer num = goods.getNum();
        if (num < 1) {
            return Result.fail().message("库存不足");
        }
        //创建订单，返回订单信息
        Long userId = UserHolder.getUser().getId();
        //在这里使用redis分布式锁
        //SimpleRedisLock lock = new SimpleRedisLock("order" + id, stringRedisTemplate);
        //使用Redisson,可重入锁
        RLock lock = redissonClient.getLock("lock:order:" + userId);
        //boolean isLock = lock.tryLock(1200);
        boolean isLock = lock.tryLock();
        if (!isLock) {
            return Result.fail().message("不允许重复下单（开挂多线程并发抢单）");
        }
        try {
            ShopGoodsService proxy = (ShopGoodsService) AopContext.currentProxy();//获取代理
            return proxy.createOrder(id);
        } finally {
            lock.unlock();
        }

    }

    @Transactional
    public Result createOrder(Long id) {
        Long userId = UserHolder.getUser().getId();
        //查询数据库，看这个人之前买过了没有，一个人只能买一个
        Integer count = shopOrderService.query().eq("buyer_id", userId).eq("goods_id", id).ne("status", 0).count();
        if (count > 0) {
            return Result.fail().message("一人一单，一个人只能买一次，你已经买过一次了哦");
        }
        //扣减库存
        //乐观锁，库存作为锁，不过库存很特殊，直接判断大于0即可
        boolean success = this.update().setSql("num = num -1").eq("id", id).gt("num", 0).update();
        if (!success) {
            return Result.fail().message("库存不足");
        }
        //创建订单
        ShopOrder shopOrder = new ShopOrder();
        long orderId = redisIdWorker.nextId("order");
        shopOrder.setId(orderId);
        shopOrder.setBuyerId(UserHolder.getUser().getId());
        shopOrder.setGoodsId(id);
        shopOrder.setStatus(1);
        shopOrderService.save(shopOrder);

        //返回订单id,跟据订单信息查询订单内容展示在网页上
        stringRedisTemplate.delete(SHOP_GOODS_KEY + id);//删除goods的redis，因为库存更新了

        //设置订单redis
        //cacheClient.set(SHOP_GOODS_KEY, shopOrder, SHOP_ORDER_TTL, TimeUnit.MINUTES); //订单设置十五分钟
        cacheClient.set(SHOP_ORDER_KEY + orderId, "", 15L, TimeUnit.MINUTES); //订单设置十五分钟
        stringRedisTemplate.opsForValue().set(SHOP_ORDER_KEY + orderId + ":data", JSONUtil.toJsonStr(shopOrder)); //订单设置十五分钟
        return Result.ok(orderId).message("已生成订单，记得去兑换~");
    }


//    版本1，这个解决了库存超卖问题，但是没有解决一人一单的问题
//    @Override
//    public Result buyGoods(Long id) {
//        Result goodsById = this.getGoodsById(id);
//        LinkedHashMap<String, Object> data = (LinkedHashMap<String, Object>) goodsById.getData();
//        //库存是否充足
//        Integer num = (Integer) data.get("num");
//        if (num < 1) {
//            return Result.fail().message("库存不足");
//        }
//        //扣减库存
//        //乐观锁，库存作为锁，不过库存很特殊，直接判断大于0即可
//        boolean success = this.update().setSql("num = num -1").eq("id", id).gt("num", 0).update();
//        if (!success) {
//            return Result.fail().message("库存不足");
//        }
//        //创建订单
//        ShopOrder shopOrder = new ShopOrder();
//        long orderId = redisIdWorker.nextId("order");
//        shopOrder.setId(orderId);
//        shopOrder.setBuyerId(UserHolder.getUser().getId());
//        shopOrder.setGoodsId(id);
//        shopOrderService.save(shopOrder);
//
//        //返回订单id,跟据订单信息查询订单内容展示在网页上
//        return Result.ok(orderId);
//    }




//    //❤❤❤❤❤❤互斥锁解决缓存击穿❤❤❤❤❤❤❤
//    //尝试获取锁
//    private boolean tryLock (String key){
//        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
//        return BooleanUtil.isTrue(flag);//这样可以防止产生空指针
//    }
//
//    //释放锁
//    private void unlock (String key){
//        stringRedisTemplate.delete(key);
//    }
//
//    //互斥锁
//    public ShopGoods queryWithMutex (Long id){
//        String key = CACHE_GOODS_KEY + id;
//        //从redis查询商铺缓存
//        String goodsJson = stringRedisTemplate.opsForValue().get(key);
//        //判断是否存在
//        if (StrUtil.isNotBlank(shopJson)) {
//            //存在，直接返回
//            return JSONUtil.toBean(goodsJson, ShopGoods.class);
//        }
//        //判断命中的是否是空值，到这里goodsJson还有两种情况，一种是“”，另一种是null
//        if (goodsJson != null) {
//            //返回一个错误信息
//            return null;
//        }
//        ShopGoods shopGoods;
//        try{
//            //实现缓存重建,获取互斥锁
//            String lockKey = LOCK_GOODS_KEY + id;
//            boolean isLock = tryLock(lockKey);
//            if (!isLock) {
//                //失败，休眠并重试
//                Thread.sleep(60);
//                return queryWithMutex(id);//调自己（重试）
//            }
//            //❤获取锁成功应该再次检测redis缓存是否存在
//            //❤做DoubleCheck，如果存在则无需重建缓存
//            //跟据id查询数据库
//            shopGoods = getById(id);
//            //判断数据库是否存在
//            if (shopGoods == null) {
//                //不存在，将空值写入redis
//                stringRedisTemplate.opsForValue().set(lockKey, "", 5, TimeUnit.MINUTES);
//                //返回错误信息
//                return null;
//            }
//            //存在，写入redis
//            stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), 30, TimeUnit.MINUTES);
//        }catch(Interrupted Exceptione){
//            throw new RuntimeException(e);
//        }finally{
//            unlock(LOCK_GOODS_KEY + id);
//        }
//        //返回
//        return shopGoods;
//    }
}
